package com.example.xiaoshuo_yudu.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.example.xiaoshuo_yudu.config.PasswordConfig;
import com.example.xiaoshuo_yudu.entity.dto.LoginDTO;
import com.example.xiaoshuo_yudu.entity.entity.Users;
import com.example.xiaoshuo_yudu.entity.vo.EmailRegisterVo;
import com.example.xiaoshuo_yudu.mapper.UsersMapper;
import com.example.xiaoshuo_yudu.service.UsersService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.example.xiaoshuo_yudu.utils.Const;
import com.example.xiaoshuo_yudu.utils.FlowUtils;

import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.util.Map;
import java.util.Random;
import java.util.concurrent.TimeUnit;

import static java.time.LocalDateTime.now;

/**
 * <p>
 * 用户基本信息表 服务实现类
 * </p>
 *
 * @author xusen
 * @since 2025-07-25
 */
@Service
public class UsersServiceImpl extends ServiceImpl<UsersMapper, Users> implements UsersService {
    @Autowired
    private FlowUtils flowUtils;
    @Autowired
    private AmqpTemplate amqpTemplate;
    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private PasswordConfig passwordConfig;
    @Autowired
    private UsersMapper usersMapper;
    @Override
    public String registerEmailVerifyCode(String type, String email, String ip) {
        // 使用IP地址作为锁对象，确保同一IP在同一时间只能有一个线程执行此方法
        synchronized (ip.intern()) {
            // 检查是否达到发送频率限制
            if (!this.VerifyLimit(ip)) {
                return "请求频繁,请稍后再试";
            }
            // 生成6位随机验证码
            Random random = new Random();
            int code = random.nextInt(899999) + 100000;
            // 创建包含验证码信息的Map对象
            Map<String, Object> date = Map.of("type", type, "email", email, "code", code);
            // 通过AMQP模板发送验证码信息到邮件队列
            amqpTemplate.convertAndSend("email", date);
            // 将验证码存储到Redis中，设置过期时间为3分钟
            redisTemplate.opsForValue().set(Const.VERIFY_EMAIL_DATA + email, String.valueOf(code), 3, TimeUnit.MINUTES);
            // 验证码发送成功，返回null
            return null;
        }
    }

    /**
     * 注册
     *
     * @param registerVo
     * @return
     */
    @Override
    public String registerEmailAccount(EmailRegisterVo registerVo) {
        // 提取用户输入的邮箱和用户名
        String email = registerVo.getEmail();
        String username = registerVo.getUsername();

        // 从Redis中获取验证邮箱的验证码
        String code = (String) redisTemplate.opsForValue().get(Const.VERIFY_EMAIL_DATA + email);
        // 如果Redis中没有该邮箱的验证码，提示用户获取验证码
        if (code == null) return "请获取验证码";
        // 如果用户输入的验证码与Redis中的验证码不匹配，提示验证码错误
        if (!code.equals(registerVo.getCode())) return "验证码有错";
        // 检查邮箱是否已被注册
        if (this.existsAccountByEmail(email)) return "邮箱已被注册，请更换";
        // 检查用户名是否已被注册
        if (this.existsAccountByUsername(username)) return "用户名已被注册，请更换";
        // 对用户输入的密码进行加密处理
        String password = passwordConfig.passwordEncoder().encode(registerVo.getPassword());
        // 创建一个新的账户对象
        Users users = new Users()
                .setUsername(username)
                .setPasswordHash(password)
                .setEmail(email)
                .setRole(1)
                .setUserStatus(1)
                .setCreateTime(now())
                .setLastLogin(null)
                .setAvatarUrl(null);
        // 尝试保存新账户信息到数据库
        if (this.save(users)) {
            // 如果保存成功，删除Redis中的验证码信息，并返回null表示注册成功
            redisTemplate.delete(Const.VERIFY_EMAIL_DATA + email);
            return null;
        } else {
            // 如果保存失败，返回错误信息
            return "注册失败请联系管理员";
        }
    }

    /**
     * 登录
     *
     * @param loginDTO
     * @return
     */
    @Override
    public Users login(LoginDTO loginDTO) {
        Users user = usersMapper.getUsersByUsername(loginDTO.getUsername());
        if (user == null){
//            抛出异常
            throw new RuntimeException("用户名不存在");
        }
        
        // 检查用户状态，禁用用户不能登录
        if (user.getUserStatus() != null && user.getUserStatus() == 0) {
            throw new RuntimeException("用户已被禁用，无法登录");
        }
        
        // 密码加密
        boolean passwordMatches = passwordConfig.passwordEncoder()
                .matches(loginDTO.getPassword(), user.getPasswordHash());
        if (!passwordMatches){
//            抛出异常
            throw new RuntimeException("密码错误");
        }
        user.setLastLogin(now());
        usersMapper.updateById(user);
        user.setPasswordHash(null);

        return user;
    }

    public boolean existsAccountByUsername(String username) {
        return this.count(new QueryWrapper<Users>().eq("username", username)) > 0;
    }

    public boolean existsAccountByEmail(String email) {
        return this.count(new QueryWrapper<Users>().eq("email", email)) > 0;

    }


    /**
     * 验证验证邮件的发送限制
     * 该方法用于检查给定IP地址在过去一分钟内是否已经发送了验证邮件
     * 主要目的是防止某个IP地址频繁发送验证邮件，以避免潜在的滥用或攻击行为
     *
     * @param ip 发送验证邮件的IP地址
     * @return 如果在过去一分钟内该IP地址发送验证邮件的次数未超过限制，则返回true；否则返回false
     */
    private boolean VerifyLimit(String ip) {
        // 构造缓存键，用于标识特定IP地址的验证邮件发送限制
        String key = Const.VERIFY_EMAIL_LIMIT + ip;
        // 调用限流工具类的方法，检查该IP地址在接下来的60秒内是否可以发送验证邮件
        return flowUtils.limitOnceCheck(key, 60);
    }
}
